﻿'
' Boogaloo® - http://www.mindfly.com
' Copyright (c) 2008
' by Mindfly, Inc.
'
' Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
' documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
' the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
' to permit persons to whom the Software is furnished to do so, subject to the following conditions:
'
' The above copyright notice and this permission notice shall be included in all copies or substantial portions 
' of the Software.
'
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
' INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
' DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
' SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
' SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
' WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
' THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Imports System.Xml
Imports System.Text
Imports System.IO
Imports System.Web
Imports System.Web.UI.WebControls
Imports System.Web.UI.HtmlControls
Imports System.Drawing
Imports System.Net
Imports System.Globalization
Imports System.Text.RegularExpressions
Imports System.Collections.Specialized

Imports Boogaloo.Core.BLL


Namespace UI

    Public Class Groove


        Public Shared Function GetEventTargetClicked(ByVal page As System.Web.UI.Page) As String
            Dim clicked As String = String.Empty
            Try
                clicked = Page.Request.Params.Get("__EVENTTARGET").Replace("$", "_")
            Catch ex As Exception
                clicked = String.Empty
            End Try
            Return clicked
        End Function

        ''' <summary>
        ''' Refreshes the current page
        ''' </summary>
        Public Shared Sub RefreshPage()
            HttpContext.Current.Response.Redirect(HttpContext.Current.Request.Url.PathAndQuery)
        End Sub

#Region "Head tags"

        Public Shared Sub AddGenericLink(ByVal page As System.Web.UI.Page, ByVal relation As String, ByVal href As String, Optional ByVal type As String = "", Optional ByVal title As String = "")

            Dim link As New HtmlLink
            If Not String.IsNullOrEmpty(type) Then link.Attributes("type") = type
            link.Attributes("rel") = relation
            If Not String.IsNullOrEmpty(title) Then link.Attributes("title") = title
            link.Attributes("href") = href
            AddControlToHeader(page, link)

        End Sub


        Public Shared Sub AddControlToHeader(ByVal page As System.Web.UI.Page, ByVal ctl As Object)

            Dim cph_head As ContentPlaceHolder = Nothing
            If page.Master IsNot Nothing Then
                Dim cphs As Dictionary(Of String, ContentPlaceHolder) = Boogaloo.Base.Reflection.ContentPlaceHolderDictionary(page.Master)
                If cphs.ContainsKey("Head") Then
                    cph_head = cphs("Head")
                ElseIf cphs.ContainsKey("head") Then
                    cph_head = cphs("head")
                End If
            End If

            If cph_head IsNot Nothing Then
                cph_head.Controls.Add(ctl)
            Else
                page.Header.Controls.Add(ctl)
            End If


        End Sub


#End Region

#Region "IO"

        ''' <summary>
        ''' Returns a list of files contained in a given directory (dir).  dir should be provided as a local system path,
        ''' ex. c:\inetpub\wwwroot\myapp\
        ''' Pattern ex. *.ascx
        ''' </summary>
        Public Shared Function GetDirectoryFiles(ByVal dir As String, Optional ByVal pattern As String = "") As String()

            If Not String.IsNullOrEmpty(pattern) Then
                Return Directory.GetFiles(dir, pattern)
            Else
                Return Directory.GetFiles(dir)
            End If

        End Function

        ''' <summary>
        ''' Quickly loads a local xml file into an XmlDocument object.  Schema is not validated
        ''' If load fails, method returns Nothing
        ''' </summary>
        Public Shared Function XmlDocumentLoad(ByVal path As String) As XmlDocument
            Dim doc As XmlDocument = Nothing
            If File.Exists(path) Then
                Try
                    doc = New XmlDocument
                    doc.Load(path)
                Catch ex As Exception
                    ' return nothing
                    doc = Nothing
                End Try                
            End If
            Return doc
        End Function

        ''' <summary>
        ''' Quickly loads an xml string into an XmlDocument object.  Schema is not validated
        ''' If load fails, method returns Nothing
        ''' </summary>
        Public Shared Function XmlDocumentLoadXml(ByVal xml As String) As XmlDocument
            Dim doc As New XmlDocument
            Try
                doc.LoadXml(xml)
            Catch ex As Exception
                doc = Nothing
            End Try
            Return doc
        End Function

        ''' <summary>
        ''' Quickly returns the contents of a text file
        ''' </summary>
        Public Shared Function GetTextFileContents(ByVal path As String) As String

            Using reader As New StreamReader(path)
                Return reader.ReadToEnd()
            End Using

        End Function

        ''' <summary>
        ''' Quickly writes the contents of a file.  File contents are overridden.
        ''' </summary>
        Public Shared Function WriteTextFileContents(ByVal path As String, ByVal contents As String) As Boolean
            Try
                Using writer As New StreamWriter(path, False, System.Text.Encoding.UTF8)
                    writer.Write(contents)
                End Using
                Return True
            Catch ex As Exception
                Return False
            End Try

        End Function

        ''' <summary>
        ''' Returns a string array of sub directories (recursive) starting at path provided
        ''' </summary>
        Public Shared Function GetFullDirectoryTree(ByVal path As String) As String()

            Dim dirs As New List(Of String)
            If Directory.GetDirectories(path).Count > 0 Then
                Dim dir As String = String.Empty
                For Each dir In Directory.GetDirectories(path)
                    Dim dirInfo As New DirectoryInfo(dir)
                    If Not ((CDbl(dirInfo.Attributes And FileAttributes.Hidden)) = FileAttributes.Hidden) And Not dirInfo.Name.StartsWith("_vti") Then dirs.AddRange(GetFullDirectoryTree(dir))
                Next
            End If

            dirs.Add(path)

            Return dirs.ToArray
        End Function

        ''' <summary>
        ''' Rewrites local system path as a virtual path.  Path must be within the application.
        ''' </summary>
        Public Shared Function RewriteServerPathAsVirtualPath(ByVal path As String, Optional ByVal assert_final_slash As Boolean = True) As String
            'Dim virtual_directory As String = IIf(HttpContext.Current.Request.ApplicationPath = "/", String.Empty, HttpContext.Current.Request.ApplicationPath)
            Dim virtualpath As String = String.Format("/{0}", path.Replace(HttpContext.Current.Server.MapPath("~/"), "").Replace("\", "/"))
            If Not virtualpath.EndsWith("/") And assert_final_slash Then virtualpath &= "/"
            Return virtualpath
        End Function

#End Region


#Region "Text methods"


        ''' <summary>
        ''' Formats a title in "title case"
        ''' </summary>
        ''' <param name="title">String</param>
        ''' <returns>Returns the string with first letter of each word capitalized</returns>
        ''' <remarks></remarks>
        Public Shared Function FormatTitleCaseString(ByVal title As String) As String
            Return CultureInfo.CurrentUICulture.TextInfo.ToTitleCase(title)
        End Function

        '''<summary>
        ''' Removes html from string
        '''  </summary>
        ''' <remarks>
        ''' http://www.4guysfromrolla.com/webtech/042501-1.shtml
        ''' </remarks>
        Public Shared Function RemoveHTMLTags(ByVal HTML As String) As String

            Dim oReg As New Regex("<(.|\n)+?>", RegexOptions.IgnoreCase)
            Dim cleaned As String = HTML

            Dim oMatch As Match = oReg.Match(HTML)
            If oMatch.Success Then

                ' replace all HTML tag matches with the empty string
                cleaned = oReg.Replace(HTML, "")

                ' Replace all < and > with &lt; and &gt;
                cleaned = cleaned.Replace("<", "&lt;").Replace(">", "&gt;")

            End If

            Return cleaned

        End Function


        ''' <summary>
        ''' Returns a string containing <a href="URL" rel="nofollow">URL</a>
        ''' inserted where there is a URL found.
        ''' URL can start with "www" or "http"
        ''' or
        ''' URL can be a email address "*@*"
        ''' </summary>
        Public Shared Function InsertHyperlinks(ByVal content As String) As String

            If Not String.IsNullOrEmpty(content) Then
                Dim strBuf As String = String.Empty
                Dim iStart As Integer = 0
                Dim iEnd As Integer = 1

                Dim oReg As Regex = New Regex("\b(www|http|\S+@)\S+\b", RegexOptions.IgnoreCase Or RegexOptions.Compiled)

                Dim match As Match = Nothing
                For Each match In oReg.Matches(content)

                    iEnd = match.Index
                    strBuf &= content.Substring(iStart, iEnd - iStart)

                    If match.Value.Contains("@") Then
                        strBuf &= String.Format("<a href=""mailto:{0}"" rel=""nofollow"">{0}</a>", match.Value)
                    Else
                        If match.Value.Contains("://") Then
                            strBuf &= String.Format("<a href=""{0}"" rel=""nofollow"">{0}</a>", match.Value)
                        Else
                            strBuf &= String.Format("<a href=""http://{0}"" rel=""nofollow"">{0}</a>", match.Value)
                        End If

                    End If
                    iStart = iEnd + match.Length
                Next

                content = strBuf & content.Substring(iStart)
            End If
            Return content
        End Function

        ''' <summary>
        ''' Regex used to find all hyperlinks.
        ''' </summary>
        Private Shared ReadOnly urls_regex As Regex = New Regex("http://([\w+?\.\w+])+([a-zA-Z0-9\~\!\@\#\$\%\^\&amp;\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?", RegexOptions.IgnoreCase) 'New Regex ("<a[^(href)]?href=""""([^""""]+)""""[^>]?>([^<]+)</a>", RegexOptions.IgnoreCase Or RegexOptions.Compiled)
        Public Shared Function GetUrlsFromContent(ByVal content As String) As List(Of Uri)
            Dim uris As New List(Of Uri)
            Dim m As Match = Nothing
            For Each m In urls_regex.Matches(content)
                Dim url As String = m.Value.Trim
                Dim u As Uri = Nothing
                If Uri.TryCreate(url, UriKind.Absolute, u) Then uris.Add(u)
            Next
            Return uris
        End Function


        ''' <summary>
        ''' Attempts to correctly format user entered text to like html format
        ''' </summary>
        ''' <param name="Value">String</param>
        ''' <returns>String</returns>
        ''' <remarks>rss - 8/12/2006</remarks>
        Public Shared Function EncodeText(ByVal Value As String) As String
            Value = HttpUtility.HtmlEncode(Value)
            Return Value.Replace("  ", "&nbsp;&nbsp").Replace(vbCrLf, "<br />")
        End Function

        ''' <summary>
        ''' Encodes a string to be represented as a string literal. The format
        ''' is essentially a JSON string.
        ''' 
        ''' Example Output: "Hello \"Rick\"!\r\nRock on"
        ''' </summary>        
        ''' <remarks>Slightly altered version of Rick Strahl's function written up here: http://www.west-wind.com/weblog/posts/114530.aspx
        ''' This version does not return the outer quotes
        ''' </remarks>
        Public Shared Function EncodeJsString(ByVal value As String) As String
            Dim sb As New StringBuilder
            Dim c As Char = Nothing
            For Each c In value
                Select Case c
                    Case "\"
                        sb.Append("\\\")
                    Case "\\"
                        sb.Append("\\\\")
                    Case "\b"
                        sb.Append("\\b")
                    Case "\f"
                        sb.Append("\\f")
                    Case "\n"
                        sb.Append("\\n")
                    Case "\r"
                        sb.Append("\\r")
                    Case "\t"
                        sb.Append("\\t")
                    Case Else
                        sb.Append(c)
                End Select
            Next
            Return sb.ToString
        End Function

#End Region


#Region "Image methods"

        ''' <summary>
        ''' Resizes and saves image in designated directory
        ''' </summary>
        ''' <param name="ImageRelativePath">relative path to existing image</param>
        ''' <param name="MaxWidth">max width in pixels, integer</param>
        ''' <param name="MaxHeight">max height in pixels, integer</param>
        ''' <param name="NewImageName">new name of image</param>
        ''' <param name="SaveLocationRelativePath">Relative path to new location of image</param>
        ''' <param name="ImgFormat">System.Drawing.Imaging.ImageFormat type indicating format of image saved (ex. Jpeg, Bmp, Tiff, etc.)</param>
        ''' <returns>Relative path to new image</returns>
        ''' <remarks>wma - 01/09/2008</remarks>
        Public Overloads Shared Function ResizeAndSaveImage(ByVal ImageRelativePath As String, ByVal MaxWidth As Integer, ByVal MaxHeight As Integer, ByVal NewImageName As String, ByVal SaveLocationRelativePath As String, ByVal ImgFormat As Imaging.ImageFormat) As String
            Try
                Dim dummyCallBack As System.Drawing.Image.GetThumbnailImageAbort = New System.Drawing.Image.GetThumbnailImageAbort(AddressOf ThumbnailCallback)
                Dim PhotoOrignal As System.Drawing.Image = System.Drawing.Image.FromFile(HttpContext.Current.Server.MapPath(ImageRelativePath))
                Dim Heightdivider As Integer = CInt(PhotoOrignal.Height / MaxHeight)
                Dim Widthdivider As Integer = CInt(PhotoOrignal.Width / MaxWidth)
                Dim Photo As System.Drawing.Image = Nothing

                If Widthdivider > Heightdivider Then
                    Photo = PhotoOrignal.GetThumbnailImage((PhotoOrignal.Width / Widthdivider), (PhotoOrignal.Height / Widthdivider), dummyCallBack, IntPtr.Zero)
                Else
                    Photo = PhotoOrignal.GetThumbnailImage((PhotoOrignal.Width / Heightdivider), (PhotoOrignal.Height / Heightdivider), dummyCallBack, IntPtr.Zero)
                End If

                Photo.Save(HttpContext.Current.Server.MapPath(SaveLocationRelativePath) & NewImageName, ImgFormat)
                PhotoOrignal.Dispose()
                Photo.Dispose()
                Return SaveLocationRelativePath & NewImageName
            Catch ex As Exception
                Return Nothing
            End Try
        End Function

        ''' <summary>
        ''' Resizes an image and returns a stram of that image
        ''' </summary>
        ''' <param name="ImageStream">stream of existing image</param>
        ''' <param name="MaxWidth">max width in pixels, integer</param>
        ''' <param name="MaxHeight">max height in pixels, integer</param>
        ''' <param name="ImgFormat">System.Drawing.Imaging.ImageFormat type indicating format of image saved (ex. Jpeg, Bmp, Tiff, etc.)</param>
        ''' <returns>stream of new image</returns>
        ''' <remarks>wma - 01/09/2008</remarks>
        Public Overloads Shared Function ResizeAndSaveImage(ByVal ImageStream As Stream, ByVal MaxWidth As Integer, ByVal MaxHeight As Integer, ByVal ImgFormat As Imaging.ImageFormat) As Stream
            Dim sr As Stream = Nothing

            Try
                Dim dummyCallBack As System.Drawing.Image.GetThumbnailImageAbort = New System.Drawing.Image.GetThumbnailImageAbort(AddressOf ThumbnailCallback)
                Dim PhotoOrignal As System.Drawing.Image = System.Drawing.Image.FromStream(ImageStream)
                Dim Heightdivider As Integer = CInt(PhotoOrignal.Height / MaxHeight)
                Dim Widthdivider As Integer = CInt(PhotoOrignal.Width / MaxWidth)
                Dim Photo As System.Drawing.Image = Nothing

                If Widthdivider > Heightdivider Then
                    Photo = PhotoOrignal.GetThumbnailImage((PhotoOrignal.Width / Widthdivider), (PhotoOrignal.Height / Widthdivider), dummyCallBack, IntPtr.Zero)
                Else
                    Photo = PhotoOrignal.GetThumbnailImage((PhotoOrignal.Width / Heightdivider), (PhotoOrignal.Height / Heightdivider), dummyCallBack, IntPtr.Zero)
                End If

                Photo.Save(sr, ImgFormat)
                PhotoOrignal.Dispose()
                Photo.Dispose()

                Return sr
            Catch ex As Exception
                Return sr
            End Try
        End Function

        Private Shared Function ThumbnailCallback() As Boolean
            Return False
        End Function

#End Region

#Region "Pingback and Trackback calls"

        ''' <summary>
        ''' Contructs the trackback url
        ''' </summary>
        Public Shared Function GetTrackbackHandlerLink(ByVal permaid As Guid) As String
            Dim trackback_url As String = CoreSettings.GetTranslatedConfigurationUrl(CoreSettings.APIs("trackback").Value)
            Return String.Format("{0}?permid={1}", trackback_url, permaid.ToString)
        End Function


        '''' <summary>
        '''' Returns the trackback RDF comment as a string
        '''' </summary>
        'Public Shared Function GetTrackbackRDFComment(ByVal n As Node) As String
        '    Dim sb As New StringBuilder
        '    sb.Append("<rdf:RDF xmlns:rdf=""http://www.w3.org/1999/02/22-rdf-syntax-ns#"" xmlns:dc=""http://purl.org/dc/elements/1.1/"" xmlns:trackback=""http://madskills.com/public/xml/rss/module/trackback/"">" & vbCrLf)
        '    sb.Append(vbTab)
        '    sb.Append(String.Format("<rdf:Description rdf:about=""{0}"" dc:identifier=""{0}"" dc:title=""{1}"" trackback:ping=""{2}"" />", n.GetLink, HttpContext.Current.Server.HtmlEncode(n.page_title), GetTrackbackHandlerLink(n.entity_key)))
        '    sb.Append(vbCrLf & "</rdf:RDF>" & vbCrLf)
        '    Return sb.ToString
        'End Function

        ''' <summary>
        ''' Regex used to find the trackback link on a remote web page.
        ''' </summary>
        ''' <remarks>Translated from BlogEngine.NET</remarks>
        ''' 
        Private Shared ReadOnly trackbackLinkRegex As Regex = New Regex("trackback:ping=""([^""]+)""", RegexOptions.IgnoreCase Or RegexOptions.Compiled)
        
        ''' <summary>
        ''' Examines the web page source code to retrieve the trackback link from the RDF.
        ''' </summary>
        ''' <remarks>Translated from BlogEngine.NET</remarks>
        Public Shared Function GetTrackbackUrlFromPage(ByVal input As String) As Uri
            Dim url As String = trackbackLinkRegex.Match(input).Groups(1).ToString().Trim()
            Dim uri As Uri = Nothing

            If uri.TryCreate(url, UriKind.Absolute, uri) Then
                Return uri
            Else
                Return Nothing
            End If
        End Function

#End Region

#Region "WebRequest"

        ''' <summary>
        ''' Downloads web page html for a given uri
        ''' </summary>
        Public Shared Function DownloadWebPage(ByVal url As Uri) As String
            Try
                Dim req As HttpWebRequest = WebRequest.Create(url)
                req.Headers("Accept-Encoding") = "gzip"
                req.Headers("Accept-Language") = "en-us"
                req.Credentials = CredentialCache.DefaultNetworkCredentials
                req.AutomaticDecompression = DecompressionMethods.GZip
                req.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US;rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3"

                Using resp As WebResponse = req.GetResponse
                    Using sr As StreamReader = New StreamReader(resp.GetResponseStream())
                        Return sr.ReadToEnd
                    End Using
                End Using

            Catch ex As Exception
                Return Nothing
            End Try
        End Function

#End Region



#Region "Properties"

        Public Shared ReadOnly Property extensions() As List(Of Component)
            Get
                Dim components As List(Of Component) = Component.GetComponents(Groove.admin_root.root_id)
                If components.Count > 1 Then
                    Dim comp As Component = Nothing
                    Dim exts As New List(Of Component)
                    For Each comp In components
                        If Not String.Equals(comp.name.ToLower.Trim, "core") Then
                            exts.Add(comp)
                        End If
                    Next
                    Return exts
                Else
                    Return New List(Of Component)
                End If
            End Get
        End Property

        Public Shared ReadOnly Property server_name() As String
            Get
                'Return HttpContext.Current.Request.ServerVariables("SERVER_NAME")
                Return HttpContext.Current.Request.Url.Host.ToString
            End Get
        End Property


        Public Shared ReadOnly Property path_code_templates() As String
            Get
                Return "~/App_Data/CodeTemplates/"
            End Get
        End Property

        Public Shared ReadOnly Property path_admin() As String
            Get

                Dim admin_dir As String = CoreSettings.GetSetting("ADMIN_Directory")
                If Not admin_dir.EndsWith("/") Then admin_dir &= "/"
                If admin_dir.StartsWith("/") And Not String.Equals(CoreSettings.VirtualPath, "/") Then admin_dir = CoreSettings.VirtualPath.Substring(0, CoreSettings.VirtualPath.Length - 1) & admin_dir
                Return admin_dir
            End Get
        End Property

        Public Shared ReadOnly Property path_admin_masters() As String
            Get
                Return String.Format("{0}Masters/", path_admin)
            End Get
        End Property

        Public Shared ReadOnly Property path_admin_controls() As String
            Get
                Return String.Format("{0}Controls/", path_admin)
            End Get
        End Property

        Public Shared ReadOnly Property path_admin_host() As String
            Get
                Return String.Format("{0}Host/", path_admin)
            End Get
        End Property


        Public Shared ReadOnly Property path_admin_site() As String
            Get
                Return String.Format("{0}Site/", path_admin)
            End Get
        End Property

        Public Shared ReadOnly Property path_admin_membership() As String
            Get
                Return String.Format("{0}Membership/", path_admin)
            End Get
        End Property

        Public Shared ReadOnly Property admin_root() As Root
            Get
                Return Root.GetAdminSiteRoot()
            End Get
        End Property

        Public Shared ReadOnly Property site_root() As Root
            Get
                Return Root.GetCurrentRoot()
            End Get
        End Property


        Public Shared ReadOnly Property publish_admin_working() As Publish
            Get
                Return Publish.GetWorkingPublish(admin_root.root_id)
            End Get
        End Property

        Public Shared ReadOnly Property publish_admin_pending() As List(Of Publish)
            Get
                Return Publish.GetPublishes(admin_root.root_id, "p")
            End Get
        End Property


        Public Shared ReadOnly Property publish_admin_current() As Publish
            Get
                Return Publish.GetCurrentPublish(admin_root.root_id)
            End Get
        End Property

        Public Shared ReadOnly Property publish_site_working() As Publish
            Get
                Return Publish.GetWorkingPublish(site_root.root_id)
            End Get
        End Property


        Public Shared ReadOnly Property publish_site_current() As Publish
            Get
                Return Publish.GetCurrentPublish(site_root.root_id)
            End Get
        End Property



        Public Shared ReadOnly Property admin_current_node() As Node
            Get
                Return Node.GetCurrentNode()
            End Get
        End Property


        Public Shared ReadOnly Property site_current_node() As Node
            Get
                Dim n As Node = Node.GetCurrentNode
                If n Is Nothing AndAlso site_root IsNot Nothing Then
                    If site_root.default_node IsNot Nothing Then
                        n = site_root.default_node
                    Else
                        n = Nothing
                    End If
                End If
                Return n
            End Get
        End Property

        Public Shared ReadOnly Property QueryString() As NameValueCollection
            Get
                Dim qs As NameValueCollection = HttpContext.Current.Request.QueryString
                Dim mod_qs As New NameValueCollection
                Dim arg As String = String.Empty
                For Each arg In qs.Keys
                    If Not String.Equals("node", arg.ToLower) And Not String.Equals("permid", arg.ToLower) Then mod_qs.Add(arg, qs(arg))
                Next

                Return mod_qs
            End Get
        End Property


#End Region

    End Class

End Namespace
